Unity Camera获取深度数据的几种方式

您所在的位置:网站首页 unity 判断当前场景 Unity Camera获取深度数据的几种方式

Unity Camera获取深度数据的几种方式

#Unity Camera获取深度数据的几种方式| 来源: 网络整理| 查看: 265

1. pipeline中的深度数据

深度是渲染管线当中的重要概念,它控制着三维世界中物体渲染的遮挡关系。我们以传统的foward render为例,三角形提交draw call以后,顶点进入vertex shader,经过model-view-projection matrix变换到齐次裁剪空间坐标系(又叫规则观察体(Canonical View Volume)中,通过透视除法(硬件自动完成)进入pixel shader(当然,现代的gpu都会在进入pixel shader之前提供Early-Z kill,后文详述)

z_ealy_z_流程.png

通过pixel shader上色以后,就使用到了本文的主角深度,深度测试阶段(Z-test)所有顶点都处在NDC坐标系(也就是整个世界规范到xy = {-1, 1}, z = {0, 1}的长方体中),如下图。

ndc_cvv_介绍.jpg

这么折腾一通,对于Z-test的意义就是所有的顶点position.z深度值都规范到{0,1}之间,这也就给比较遮挡关系提供了便利,距离越近的点才能渲染到render texture中,距离太远的话当前像素就可以丢弃了。距离相机越近的点z值越小,假如我们使用如下的pixel shader来表示深度图

fixed4 frag (v2f i) : SV_Target { float depth = tex2D(_CameraDepthTexture, i.uv).r; float4 col = float4(depth, depth, depth, 1); return col }

由于rgb一致,所以深度图都是以灰度图呈现。

2. UNITY 获取深度图 -- camera的内置depth texture

Camera可以生成depth texture, depth+normals texture,这些内置数据可以用于延迟渲染以及shadow map,本文主要讨论深度图,其他概念暂且摁下不表。

获取Camera内置深度图的介绍的比较多,demo可以参考这个例子,github需要翻墙,本文也使用这个场景做其他获取方式的介绍。

内置的深度图名字为_CameraDepthTexture,我们在使用它的时候需要遵循如下流程

抓取内置depth texture的获取基本流程如下~ 获取深度图需要指定Camera, 设置抓取深度图的Camera.depthTextureMode = DepthTextureMode.Depth; 声明一个rendertexture rt用于保存深度图,rt = RenderTexture.GetTemporary(width, height, 0); 在Camera的Gameobj身上挂载一个script,重载 OnRenderImage方法 OnRenderImage中 Graphics.Blit(source, dest, DepthRender); 保证场景中希望写入深度图的gameobj,它们上色的shader必须有 FallBack "Diffuse"代码块,否则深度图不会将他的深度数据写入

流程1说明需要向camera中保存depth tetxure到_CameraDepthTexture中。 流程2是保存深度图的容器 当场景绘制完成以后,我们使用blit方法将数据拷贝到dest中去,请注意!!_CameraDepthTexture与blit(src,dest)中的src,dest位置顺序没有关系,_CameraDepthTexture中一直存在深度数据,_MainTex的数据则只来自src 流程5是一个潜规则,原文是这样的Depth texture is rendered using the same shader passes as used for shadow caster rendering (ShadowCaster pass type). 。

简单来说内置深度数据来自shadow map计算时使用的一个特殊pass,我们必须设置这个pass才能让内置depth buffer完成数据填充,FallBack "Diffuse"在这个时候就起到了作用,貌似自己写的其他pass时不会在获取内置深度时调用。大坑一个,所以自己写的shader结构应该保留FallBack "Diffuse":

Shader "Custom/yourshader" { Properties { _MainTex ("Base (RGB)", 2D) = "white" {} } SubShader { Tags { "RenderType"="Opaque" } Pass { } } FallBack "Diffuse" }

然后我们来看流程4的DepthRender,它的作用就是从_CameraDepthTexture获取depth输出给blit的dest,完整代码如下

Shader "Unlit/depth_render" { Properties { _MainTex ("Texture", 2D) = "black" {} } SubShader { // No culling or depth Cull Off ZWrite Off ZTest Always Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag sampler2D _CameraDepthTexture; sampler2D _MainTex; struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; float4 vertex : SV_POSITION; }; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = v.uv; return o; } fixed4 frag (v2f i) : SV_Target { float4 col = float4(1, 1, 1, 1); // 内置 深度获取 float depth = tex2D(_CameraDepthTexture, i.uv).r; col = float4(depth, depth, depth, 1 ); return col; } ENDCG } } }

其中获取深度还可以使用unity的内置宏SAMPLE_DEPTH_TEXTURE,一般情况下就是取texture的r分量即可 #define SAMPLE_DEPTH_TEXTURE(sampler, uv) (tex2D(sampler, uv).r)

tips -- 由于是image effect,我们关闭深度测试和裁剪**Cull Off ZWrite Off ZTest Always**, 关于内置宏,请自行下载unity-build-in-shader查看

深度图2.png

左边是scene view 右边是深度图,我们可以得出这样的结论

物体距离摄像机越近,内置深度图的深度值就越大(越接近1 - 白色) 3. UNITY 获取深度图 -- 创建一个专门的depth camera

上文使用的是unity的内置方法获取深度图,那么我们通过自己的shader将vertex的z值写入rt,当然就可以让一个camera专门渲染深度了!!~~~

渲染深度数据的shader如下

Shader "Unlit/deptVisual" { Properties { _MainTex ("Texture", 2D) = "white" {} } SubShader { Tags { "RenderType"="Opaque" } LOD 100 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; float4 vertex : SV_POSITION; }; sampler2D _MainTex; float4 _MainTex_ST; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex); return o; } fixed4 frag (v2f i) : SV_Target { return fixed4(i.vertex.z, i.vertex.z, i.vertex.z, 1); } ENDCG } } }

我们看一下渲染结果

深度图3.png

但是这个方法带来一个问题,难道我们为了获取深度数据,难道还要让所有的game obj 替换成上述的material吗?答案当然是否定的,这时候就需要介绍一个camera的大杀器了

替换渲染Camera.renderwithshader()

讲一个例子,假如场景中存在两个camera,renderCamera和depthCamera,场景中其他的gameobj都是默认的材质。那么我们为了不影响renderCamera的正常渲染,depthCamera如何不修改game obj的材质但是又能使用上述的deptVisual-shader进行深度图提取呢?方式就是RenderWithShader(Shader replaceShader, string replacementTag) 它需要两个参数,头一个是替换执行的shader,很好理解,第二个是决定如何替换的规则,它一般都是使用"RenderType"。我们从unity doc可以知道每一个subshader都可以设置RenderType

Tags { "RenderType"="Opaque" }

这里rendertype的具体用法就是:replaceShader中所有RenderType的值 = originShader中RenderType的值的,都会进行替换渲染,也就是本来用originShader subshader 渲染的,因为originShader subshader 的RenderType与replaceShader subshader的一致,渲染都改为使用replaceShader subshader。 假如第二个参数 = "",则此摄像机本次渲染的所有物体都会使用replaceShader 进行渲染。

replaceShader 中Tags { "RenderType"="Opaque" },就是所有的不透明物体都是用replaceShader渲染一次。我们使用renderwithshader把所有不透明的物体进行深度提取(半透明物体的深度zwrite off)。

unity的官网里已经有了相关代码,c# 代码如下

using UnityEngine; using System.Collections; [RequireComponent(typeof(Camera))] public class DepthRenderer : MonoBehaviour { GameObject depthCamera=null; Shader replacementShader=null; // Use this for initialization void Start () { depthCamera=new GameObject(); depthCamera.AddComponent(); depthCamera.camera.enabled=false; depthCamera.hideFlags=HideFlags.HideAndDontSave; depthCamera.camera.CopyFrom(camera); depthCamera.camera.cullingMask=1


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3